﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Script.Serialization;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using Vision.Common;

namespace Vision.Utilities.DataAnalytics
{
    public partial class PrtgAnalyticsForm : Form
    { 
        #region Ctor
        public PrtgAnalyticsForm()
        {
            InitializeComponent();
        }
        #endregion

        #region Form Load
        private void PrtgAnalyticsForm_Load(object sender, EventArgs e)
        { 
            PRTGUrlTextBox.Text = "https://prtg.visioninternet.com/";
            UserNameTextBox.Text = "Visionrd";
            PasswordTextBox.Text = "Visionrd1";
            FileFormatComboBox.DataSource = new KeyValuePair<string, string>[]
            {
                new KeyValuePair<string,string>("xml","XML"),
                //new KeyValuePair<string,string>("csv","CSV")
            };
            FileFormatComboBox.ValueMember = "Key";
            FileFormatComboBox.DisplayMember = "Value";
            FileFormatComboBox.SelectedValue = "xml";


            LiveComboBox.DataSource = new KeyValuePair<string, string>[]
            {
                new KeyValuePair<string,string>("0","History Data"),
                new KeyValuePair<string,string>("144","Latest 12 hours"),
                new KeyValuePair<string,string>("72","Latest 6 hours"),
                new KeyValuePair<string,string>("216","Latest 18 hours"),
                new KeyValuePair<string,string>("288","Latest 24 hours"),
            };
            LiveComboBox.ValueMember = "Key";
            LiveComboBox.DisplayMember = "Value";
            LiveComboBox.SelectedValue = "144";

            var dateTimeNow = DateTime.Now.AddDays(-1);
            StartDateTimePicker.MaxDate = dateTimeNow;
            EndDateTimePicker.MaxDate = dateTimeNow;
            var endDate = new DateTime(dateTimeNow.Year, dateTimeNow.Month, dateTimeNow.Day, dateTimeNow.Hour, 0, 0);
            StartDateTimePicker.Value = endDate.Subtract(new TimeSpan(7, 0, 0, 0));
            EndDateTimePicker.Value = endDate;
            AverageIntervalTextBox.Text = "300";
        }
        #endregion

        #region GetSensor Button Event
        private void GetCms45ClientSiteInfo()
        {
            var apiClient = GetPrtgApiClient();
            var getSensorData = apiClient.GetSensorData();

            /* CMS4, CMS5 */
            var cms45ClientSensors = getSensorData.Sensors
                                               .Where(c => c.Device != null
                                                        && (c.Device.StartsWith("CMS4", StringComparison.InvariantCultureIgnoreCase)
                                                         || c.Device.StartsWith("CMS5", StringComparison.InvariantCultureIgnoreCase))
                                                        && c.Group != null
                                                        && c.Group.Equals("WEB SITES STATUS")
                                                        && c.Type.StartsWith("HTTP", StringComparison.InvariantCultureIgnoreCase))
                                               .ToList();
            var list = new List<Tuple<string, string, string>>();
            foreach (var site in cms45ClientSensors)
            {
                var url = apiClient.GetSensorHttpURL(site.ID);
                if (!string.IsNullOrEmpty(url))
                {
                    var remoteIP = GetRemoteIPAddress(url);
                    list.Add(Tuple.Create<string, string, string>(site.ID, url, remoteIP));
                }
            }
            // we can compare the Final URL, Vision URL to know whether it's still hosted by Vision.
            var finalJSON = new JavaScriptSerializer().Serialize(list.Select(a =>
            {
                var site = cms45ClientSensors.Where(b => b.ID.Equals(a.Item1)).FirstOrDefault();
                return new
                {
                    siteName = site.Name,
                    VisionURL = a.Item2,
                    Server = site.Device,
                    FinalURL = a.Item3,
                };
            }).OrderBy(a => a.FinalURL).ToList());
        }

        private void GetSensorsButton_Click(object sender, EventArgs e)
        {
            var apiClient = GetPrtgApiClient();
            var getSensorData = apiClient.GetSensorData();
        
            var cms6SiteSensors = getSensorData.Sensors
                                               .Where(c => c.Device != null
                                                        && c.Device.StartsWith("CMS6", StringComparison.InvariantCultureIgnoreCase)
                                                        && !c.Device.Equals("cms6-vmc", StringComparison.InvariantCultureIgnoreCase)
                                                        && c.Group!=null
                                                        && c.Group.Equals("WEB SITES STATUS")
                                                        && c.Type.StartsWith("HTTP", StringComparison.InvariantCultureIgnoreCase))
                                               .ToList();

            SensorsCheckedListBox.Items.Clear();
            foreach (var sensor in cms6SiteSensors.OrderBy(c=>c.Name))
            {
                SensorsCheckedListBox.Items.Add(sensor, true);
            }

            TotalLabel.Text = string.Format("Total: {0}.", cms6SiteSensors.Count);
            SelectedSensorsLabel.Text = string.Format("Selected: {0}.", SensorsCheckedListBox.CheckedItems.Count);
        }
        #endregion

        #region Export
        private void ExportButton_Click(object sender, EventArgs e)
        {
            var analyticsResult = new List<PrtgAnalyticsResponseTimeData>();
            var responseTimeGoal1 = 1000;
            var responseTimeGoal2 = 2000;
            var averageRemoval = string.IsNullOrEmpty(AverageRemovalTextBox.Text) ? 0 : int.Parse(AverageRemovalTextBox.Text);
            if (averageRemoval >= 50 || averageRemoval<=0)
                averageRemoval = 0;

            var count = SensorsCheckedListBox.CheckedItems.Count;
            var index = 0;
            progressBar1.Minimum = 0;
            progressBar1.Maximum = count;
            progressBar1.Value = 0;
            var originalDataList = new List<PrtgResponseTimeData>();

            var liveCount = GetSelectLiveCount();

            var prtgApiClient = GetPrtgApiClient(); 
            foreach (var item in SensorsCheckedListBox.CheckedItems)            
            {
                if (index == count)
                    break;

                var sensor = item as PrtgSensorData;
                progressBar1.Value = index+1;
                var list = new List<PrtgResponseTimeData>();

                if (liveCount <= 0)
                {
                    list = prtgApiClient.GetPrtgResponseTimeDataList(sensor.ID,
                                                                         StartDateTimePicker.Value,
                                                                         EndDateTimePicker.Value,
                                                                         int.Parse(AverageIntervalTextBox.Text),
                                                                         FileFormatComboBox.SelectedValue.ToString())
                                            .OrderBy(c => c.ResponseTime)
                                            .Where(c => c.ResponseTime > 0)
                                            .ToList();
                }
                else
                {
                    list = prtgApiClient.GetLivePrtgResponseTimeDataList(sensor.ID,
                                                                        liveCount,
                                                                        FileFormatComboBox.SelectedValue.ToString()
                                                                        )
                                              .OrderBy(c => c.ResponseTime)
                                              .Where(c => c.ResponseTime > 0)
                                              .ToList();
                }

                var totalCount = list.Count();

                var analytics = new PrtgAnalyticsResponseTimeData()
                {
                    ID=  sensor.ID,
                    SiteName = sensor.Name,      
                    HostName = sensor.URL
                };

                if (totalCount > 0)
                {
                    var averageStart = (int)(totalCount * averageRemoval / 100.0);
                    var averageCount = (int)(totalCount * (100 - 2 * averageRemoval) / 100.0);

                    analytics.Average = list.Skip(averageStart).Take(averageCount).Sum(c => c.ResponseTime) / averageCount;

                    analytics.Percentage20 = GetPecentageData(list, 0.2f).ResponseTime;
                    analytics.Percentage50 = GetPecentageData(list, 0.5f).ResponseTime;
                    analytics.Percentage90 = GetPecentageData(list, 0.9f).ResponseTime;
                    analytics.Percentage95 = GetPecentageData(list, 0.95f).ResponseTime;
                    analytics.Min = list.Min(c => c.ResponseTime);
                    analytics.Max = list.Max(c => c.ResponseTime);
                    analytics.TotalLessThanGoal1000 = list.Count(c => c.ResponseTime <= responseTimeGoal1);
                    analytics.TotalLessThanGoal2000 = list.Count(c => c.ResponseTime <= responseTimeGoal2);
                    analytics.Total = totalCount;
                }
                analyticsResult.Add(analytics);

                originalDataList.AddRange(list);
                index++;
            }

            WriteResponseTimeAnalyticsIntoCsv(originalDataList, analyticsResult.ToArray());

            MessageBox.Show("Exported Success.");
        }
        #endregion

        #region Select All
        private void SelectAllCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            for (var index = 0; index < SensorsCheckedListBox.Items.Count; index++)
                SensorsCheckedListBox.SetItemChecked(index, SelectAllCheckBox.Checked);
            SelectedSensorsLabel.Text = string.Format("Selected: {0}.", SensorsCheckedListBox.CheckedItems.Count);
        }
        #endregion

        #region Helper Methods
        PrtgApiClient GetPrtgApiClient()
        {
            return new PrtgApiClient(PRTGUrlTextBox.Text, UserNameTextBox.Text, PasswordTextBox.Text);
        }
        
        T GetPecentageData<T>(List<T> orderedDataList, float pecentage)
        {
            if (!orderedDataList.Any())
                return default(T);
            if(pecentage>=1)
                pecentage =1;
            var index = (int)(pecentage * orderedDataList.Count);
            if (index == orderedDataList.Count)
                index = orderedDataList.Count - 1;
            return orderedDataList[index];
        }

        void WriteResponseTimeAnalyticsIntoCsv(IEnumerable<PrtgResponseTimeData> oringalData, params PrtgAnalyticsResponseTimeData[] dataList)
        {
            if (dataList.Any())
            {
                var tempFileName = string.Format("Prtg_ResponseAnalytics_{0}.csv", DateTime.Now.ToString("yyyyMMddHHmmss"));
                var exeDirectory = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
                var tempFilePath = System.IO.Path.Combine(exeDirectory, tempFileName);

                var carriageReturn = "\r\n";
                var commaStr = ",";

                var responseTimeGoal1 = 1000;
                var responseTimeGoal2 = 2000;
                using (var streamWriter = new StreamWriter(tempFilePath, false))
                {
                    var resultBuilder = new StringBuilder();
                    resultBuilder.AppendFormat("Id,Site Name,Host Name,Average,Min,Max,20%,50%,90%,95%,Total Tests,<{0} msc %,<{1} msc %", responseTimeGoal1, responseTimeGoal2); 
                    foreach (var data in dataList)
                    {
                        resultBuilder.Append(carriageReturn);
                        resultBuilder.Append(data.ID);
                        resultBuilder.AppendFormat("{0}\"{1}\"", commaStr, data.SiteName);
                        resultBuilder.AppendFormat("{0}{1}", commaStr, data.HostName);
                        resultBuilder.AppendFormat("{0}{1}", commaStr, data.Average);
                        resultBuilder.AppendFormat("{0}{1}", commaStr, data.Min);
                        resultBuilder.AppendFormat("{0}{1}", commaStr, data.Max);
                        resultBuilder.AppendFormat("{0}{1}", commaStr, data.Percentage20);
                        resultBuilder.AppendFormat("{0}{1}", commaStr, data.Percentage50);
                        resultBuilder.AppendFormat("{0}{1}", commaStr, data.Percentage90);
                        resultBuilder.AppendFormat("{0}{1}", commaStr, data.Percentage95);
                        resultBuilder.AppendFormat("{0}{1}", commaStr, data.Total);
                        //resultBuilder.AppendFormat("{0}{1}", commaStr, data.TotalLessThanGoal);
                        resultBuilder.AppendFormat("{0}{1}", commaStr, (100.0f * data.TotalLessThanGoal1000) / data.Total);
                        resultBuilder.AppendFormat("{0}{1}", commaStr, (100.0f * data.TotalLessThanGoal2000) / data.Total);
                    }
                    resultBuilder.Append(carriageReturn);
                    streamWriter.Write(resultBuilder.ToString());
                }

                using (var streamWriter = new StreamWriter(tempFilePath + ".xml", false))
                {
                    var xmlSerializer = new XmlSerializer(typeof(List<PrtgResponseTimeData>));
                    xmlSerializer.Serialize(streamWriter, oringalData.OrderBy(c=>c.SiteID).ThenBy(c=>c.DateTimeRaw).ToList());
                }
            }
        }

        string GetRemoteIPAddress(string url)
        {
            try
            {
                IPEndPoint remoteEP = null;

                var currentUri = url;
                while (true)
                {
                    var request = (HttpWebRequest)WebRequest.Create(currentUri);
                    request.Method = "GET";
                    request.ServicePoint.Expect100Continue = false;
                    request.ContentType = "application/x-www-form-urlencoded";
                    request.AllowAutoRedirect = true;
                    request.ServicePoint.BindIPEndPointDelegate = delegate(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount)
                    {
                        remoteEP = remoteEndPoint;
                        return null;
                    };
                    var response = request.GetResponse();
                    if (response.ResponseUri.AbsoluteUri.Equals(request.RequestUri.AbsoluteUri, StringComparison.CurrentCultureIgnoreCase))
                    {
                        return remoteEP != null ? remoteEP.Address.ToString() : string.Empty;
                    }
                    else
                    {
                        currentUri = response.ResponseUri.AbsoluteUri;
                    }
                }
            }
            catch
            { 
            }
            return string.Empty;
        }
        #endregion

        #region Live ComboBox Changed
        private void LiveComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (GetSelectLiveCount() > 0)
            {
                StartDateTimePicker.Enabled = false;
                EndDateTimePicker.Enabled = false;
            }
            else
            {
                StartDateTimePicker.Enabled = true;
                EndDateTimePicker.Enabled = true;
            }
        }
        #endregion

        #region GetSelectLiveCount
        protected int GetSelectLiveCount()
        {
            var selectLiveValue = LiveComboBox.SelectedValue;
            if (selectLiveValue is KeyValuePair<string, string>)
            {
                return int.Parse(((KeyValuePair<string, string>)selectLiveValue).Key);
            }
            else
            {
                return int.Parse(selectLiveValue.ToString());
            }
        }
        #endregion

        private void SensorsCheckedListBox_SelectedValueChanged(object sender, EventArgs e)
        {
            SelectedSensorsLabel.Text = string.Format("Selected: {0}.", SensorsCheckedListBox.CheckedItems.Count);
        }
    }
}
